﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using rkShared.Serialization;
using System.Reflection;

namespace rkShared.BEncode
{
    public class BEncodedDictionary : Dictionary<string, IBElement>, IBElement
    {
        public BEncodedDictionary()
            : base(StringComparer.InvariantCultureIgnoreCase)
        {
        }
        #region IBElement Members

        public void Encode(BinaryWriter writer)
        {
            if (this.Keys.Count > 0)
            {
                writer.Write('d');
                foreach (string key in this.Keys)
                {
                    IBElement _key = new BEncodedString(Encoding.UTF8.GetBytes(key));
                    IBElement _value = this[key];

                    _key.Encode(writer);
                    _value.Encode(writer);
                }
                writer.Write('e');
            }
        }
        public void Decode(byte[] binary)
        {
            MemoryStream _stream = new MemoryStream(binary);
            BinaryReader _reader = new BinaryReader(_stream);

            _reader.Close();
            this.Decode(_reader);
        }
        public void Decode(BinaryReader reader)
        {
            reader.ReadChar(); //Read 'd'
            BEncodedDictionary _dict = BEncodedDocument.DecodeDictioanary(reader) as BEncodedDictionary;

            foreach (string key in _dict.Keys)
                this.Add(key, _dict[key]);
        }
        #endregion
        /// <summary>
        /// Adds a key value pair to dictionary. Encodes the value with System.Encoding.ASCII while serializing.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Add(string key, string value)
        {
            this.Add(key, value, Encoding.ASCII);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="e">Encoding to use when encoding value</param>
        public void Add(string key, string value, Encoding e)
        {
            this.Add(key, new BEncodedString(value, e));
        }
        public void Add(string key, int value)
        {
            this.Add(key, (IBElement)new BEncodedInt(value));
        }
        public void Add(string key, IBinarySerializable data)
        {
            BEncodedDictionary _dataDict = new BEncodedDictionary();
            data.SerializeToDictionary(_dataDict);
            this.Add(key, _dataDict);
        }
        public int GetInt(string key)
        {
            return (int)((BEncodedInt)this[key]).Value;
        }
        public string GetString(string key)
        {
            return this.GetString(key, Encoding.ASCII);
        }
        public string GetString(string key, Encoding e)
        {
            return ((BEncodedString)this[key]).ToString(e);
        }
        public T GetIBinarySerializable<T>(string key) where T : IBinarySerializable
        {
            BEncodedDictionary _dict = this[key] as BEncodedDictionary;
            ConstructorInfo _ci =  typeof(T).GetConstructor(Type.EmptyTypes);
            IBinarySerializable _obj = _ci.Invoke(null) as IBinarySerializable;


            _obj.DeserializeFromDictionary(_dict);
            return (T)_obj;
        }
    }
}
